﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Globalization;

namespace Wykres_sieci_neuronowych
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Wczytajdane();
        }

        double[][][][] weights; //[LMax][eMax[i], nMax[i], wMax[i]]
        double[][][][] signalY; //[LMax][eMax[i], vectMax[i], nMax[i]]
        double[][][] srodki;
        int promien = 100;
        int przestrzen = 200;


        private void Wczytajdane()
        {
            StreamReader signalReader = new StreamReader(@"iris_std_sh_signals.txt");
            StreamReader weightReader = new StreamReader(@"iris_std_sh_weights.txt");
            StreamReader reader = new StreamReader(@"iris_std_sh.txt");
            CultureInfo culture = new CultureInfo("en-us");
            #region Czytanie signals i warstwy wejściowej
            int iloscLiniiSignal = 0;
            int eMax = 0;
            int LMax = 0;
            int vectMax = 0;
            int licznik = 0;
            double[][][] daneSignal;
            string linia;
            string ostatniaLinia = "";
            string[] temp;
            char[] spacja = { ' ' };
            List<int> ilosciNeuronow = new List<int>();

            while ((linia = signalReader.ReadLine()) != null) //zliczanie linii
            {
                iloscLiniiSignal++;
                ostatniaLinia = linia;
            }

            temp = ostatniaLinia.Split(spacja, StringSplitOptions.RemoveEmptyEntries);
            vectMax = Convert.ToInt32(temp[0]) + 1; //+1 bo w pliku numerowane od zera
            eMax = (iloscLiniiSignal - 1) / (2 + vectMax);
            signalReader.Close();

            signalReader = new StreamReader(@"iris_std_sh_signals.txt");
            linia = signalReader.ReadLine(); //wczytanie danych z nagłówka
            for (int i = 9; i < linia.Length - 6; i += 2)
            {
                ilosciNeuronow.Add(Convert.ToInt32(linia[i] - 48)); //char->int
                licznik++;
            }
            LMax = licznik;
            signalY = new double[LMax][][][];
            for (int i = 0; i < signalY.GetLength(0); i++)
            {
                signalY[i] = new double[eMax][][];
                for (int j = 0; j < eMax; j++)
                {
                    signalY[i][j] = new double[vectMax][];
                    for (int k = 0; k < vectMax; k++)
                    {
                        signalY[i][j][k] = new double[ilosciNeuronow[i]];
                    }
                }
            }

            #region warstwa wejsciowa
            //tutaj czyta irisa
            reader.ReadLine();//pominiecie naglowka
            for (int i = 0; i < vectMax; i++)
            {
                linia = reader.ReadLine();
                temp = linia.Split(spacja, StringSplitOptions.RemoveEmptyEntries);
                for (int j = 0; j < eMax; j++)
                {
                    for (int k = 0; k < ilosciNeuronow[0]; k++)
                    {
                        signalY[0][j][i][k] = Double.Parse(temp[k], culture);
                    }
                }
            }
            #endregion

            daneSignal = new double[eMax][][];
            for (int i = 0; i < eMax; i++) //for po epokach
            {
                daneSignal[i] = new double[vectMax][];
                linia = signalReader.ReadLine();//pominiecie "epoch=x"
                linia = signalReader.ReadLine();//pominiecie nazw kolumn
                for (int j = 0; j < vectMax; j++) //for po wektorach
                {
                    linia = signalReader.ReadLine();
                    temp = linia.Split(spacja, StringSplitOptions.RemoveEmptyEntries);
                    daneSignal[i][j] = new double[temp.Length - 1];
                    for (int k = 1; k < temp.Length; k++) //pominiecie numeru wektora
                    {
                        daneSignal[i][j][k - 1] = Double.Parse(temp[k], culture);
                    }
                }
            }
            //daneSignal[emax][vectmax][nmax[i]*lmax]
            //signalY[lmax-1][emax][vectmax][nmax]

            for (int i = 0; i < eMax; i++)
            {
                for (int j = 0; j < vectMax; j++)
                {
                    int pozycja = 0;
                    for (int k = 1; k < ilosciNeuronow.Count; k++)
                    {
                        for (int l = 0; l < ilosciNeuronow[k]; l++)
                        {
                            signalY[k][i][j][l] = daneSignal[i][j][pozycja];
                            pozycja++;
                        }
                    }
                }
            }

            #endregion

            #region Czytanie weights
            weights = new double[LMax - 1][][][]; //LMax-1,eMax,nMax[L],nMax[L-1]+1   w pliku jest string emax,lmax,nmax,wmax
            string[][][][] tempWeights = new string[eMax][][][];
            //4-4-5-3 -> ostatni element to 3/2/5. 4-ta warstwa, trzeci neuron, bias (ostatnia waga)
            //warstw jest LMax - 1. Neuronów tyle ile ilosciNeuronow[L]. Wag jest tyle ile ilosciNeuronow[L-1]+1
            //L liczymy od 1 do LMax bo pomijamy zerową warstwę
            #region inicjalizowanie weights[][][][]
            for (int i = 1; i <= weights.GetLength(0); i++)//LMax-1
            {
                weights[i - 1] = new double[eMax][][];
                for (int j = 0; j < weights[i - 1].GetLength(0); j++)
                {
                    weights[i - 1][j] = new double[ilosciNeuronow[i]][];
                    for (int k = 0; k < weights[i - 1][j].GetLength(0); k++)
                    {
                        weights[i - 1][j][k] = new double[ilosciNeuronow[i - 1] + 1];
                    }
                }
            }
            #endregion
            #region inicjalizowanie tempWeights[][][][]
            for (int i = 0; i < tempWeights.GetLength(0); i++)//emax
            {
                tempWeights[i] = new string[LMax - 1][][];
                for (int j = 1; j <= tempWeights[i].GetLength(0); j++)
                {
                    tempWeights[i][j - 1] = new string[ilosciNeuronow[j]][];
                    for (int k = 0; k < tempWeights[i][j - 1].GetLength(0); k++)
                    {
                        tempWeights[i][j - 1][k] = new string[ilosciNeuronow[j - 1] + 1];
                    }
                }
            }
            #endregion
            linia = weightReader.ReadLine();//pominiecie "Network=..."
            linia = weightReader.ReadLine();//pominiecie nazw kolumn
            #region Czytanie pliku
            for (int i = 0; i < eMax; i++)
            {
                linia = weightReader.ReadLine();
                #region Cięcie linijki
                temp = linia.Split(spacja, StringSplitOptions.RemoveEmptyEntries); //temp ma 64 pola. [0] to nr epoki
                // w kazdej linii (epoce) for na warstwy. w kazdej warstwie for na neurony, w kazdym neuronie for na wagi
                int iterator = 1;
                for (int j = 1; j < LMax; j++)
                {
                    for (int k = 0; k < ilosciNeuronow[j]; k++)
                    {
                        for (int l = 0; l < ilosciNeuronow[j - 1] + 1; l++)
                        {
                            tempWeights[i][j - 1][k][l] = temp[iterator];
                            iterator++;
                        }
                    }
                }
                #endregion
            }
            #region Przepisywanie do weights
            //weights[LMax-1][eMax][nMax[L]][nMax[L-1]+1]
            //tempweights[eMax][LMax-1][nMax[L]][nMax[L-1]+1]
            for (int i = 1; i <= LMax - 1; i++)
            {
                for (int j = 0; j < eMax; j++)
                {
                    for (int k = 0; k < ilosciNeuronow[i]; k++)
                    {
                        for (int l = 0; l < ilosciNeuronow[i - 1] + 1; l++)
                        {
                            weights[i - 1][j][k][l] = Double.Parse(tempWeights[j][i - 1][k][l], culture);
                        }
                    }
                }
            }
            #endregion
            #endregion
            #endregion
        }

        public Ellipse StworzKolo(double sygnalWyjsciowy)
        {
            Ellipse neuron = new Ellipse();
            Color kolor = new Color();
            if (sygnalWyjsciowy >= 0) //dodatni czy ujemny sygnal wyjsciowy
            {
                kolor = Colors.Red;
            }
            else
            {
                kolor = Colors.SkyBlue;
            }
            neuron.Width = promien * Math.Sqrt(Math.Abs(sygnalWyjsciowy)); //rozmiar zależny od wartości
            neuron.Height = promien * Math.Sqrt(Math.Abs(sygnalWyjsciowy));
            neuron.StrokeThickness = 1;
            neuron.Stroke = Brushes.Black;
            neuron.Fill = new SolidColorBrush(kolor);
            neuron.MouseEnter += MyMouseEnter;
            neuron.MouseLeave += MyMouseLeave;
            return neuron;
        }

        public void UmiescKolo(ref Ellipse kolo, int i, int j, double maxJ, ref double[] srodek)
        {
            double temp = (promien - kolo.Height) / 2.0;
            Canvas.SetTop(kolo, temp + przestrzen * i + 10);
            srodek[1] = temp + przestrzen * i + kolo.Height / 2.0;
            Canvas.SetLeft(kolo, temp + promien * j + ((Plotno.Width - maxJ * promien) / 2.0));
            srodek[0] = temp + promien * j + kolo.Width / 2.0 + ((Plotno.Width - maxJ * promien) / 2.0);
            Canvas.SetZIndex(kolo, 1);
        }

        public int MaxN(double[][][][] wagi)
        {
            int wynik = Int32.MinValue;
            for (int i = 0; i < wagi.GetLength(0); i++)
            {
                for (int j = 0; j < wagi[i].GetLength(0); j++)
                {
                    if (wynik < wagi[i][j].GetLength(0))
                    {
                        wynik = wagi[i][j].GetLength(0);
                    }
                }
            }
            return wynik;
        }

        public void Schemat(double[][][][] wagi, double[][][][] sygnaly)
        {
            textBlock1.Text = "";
            textBlock1.Text = "";
            Plotno.Children.Clear();
            Plotno.Height = (wagi.GetLength(0) + 2) * (przestrzen - promien / 2.0);
            Plotno.Width = (MaxN(wagi)) * promien;
            SiatkaGlowna.InvalidateVisual();
            int e = Convert.ToInt32(textBox1.Text);
            int vect = Convert.ToInt32(textBox2.Text);
            int LMax = sygnaly.GetLength(0);
            srodki = new double[LMax][][];
            bool wypisanoBlad = false;
            for (int i = 0; i < LMax; i++)
            {
                int eMax = sygnaly[i].GetLength(0);
                if (e >= eMax || e<0) //zabezpieczenie
                {
                    MessageBox.Show("Błąd! W pliku danych jest tylko " + eMax + " epok.", "Błąd", MessageBoxButton.OK, MessageBoxImage.Stop);
                    return;
                }
                for (int j = 0; j < eMax; j++)
                {
                    int vectMax = sygnaly[i].GetLength(0);
                    if (vect > vectMax || vect<0) //zabezpieczenie
                    {
                        if (!wypisanoBlad)
                        {
                            MessageBox.Show("Błąd! W pliku danych jest tylko " + vectMax + " wektorów.", "Błąd", MessageBoxButton.OK, MessageBoxImage.Stop);
                            wypisanoBlad = true;
                        }
                        return;
                    }

                    int nMax = signalY[i][j][vectMax].GetLength(0);
                    Ellipse[] neurony = new Ellipse[nMax];
                    srodki[i] = new double[nMax][];

                    for (int k = 0; k < nMax; k++)
                    {
                        neurony[k] = StworzKolo(sygnaly[i][e][vect][k]);
                        srodki[i][k] = new double[2];
                        UmiescKolo(ref neurony[k], i, k, nMax, ref srodki[i][k]);
                        Plotno.Children.Add(neurony[k]);
                        if (i > 0)
                        {
                            for (int l = 0; l < signalY[i - 1][j][vectMax].GetLength(0); l++)
                            {
                                Line test = new Line();
                                if (weights[i - 1][e][k][l] >= 0)
                                {
                                    test.Stroke = System.Windows.Media.Brushes.Red;
                                }
                                else
                                {
                                    test.Stroke = System.Windows.Media.Brushes.Blue;
                                }
                                test.StrokeThickness = Math.Sqrt(Math.Abs(weights[i - 1][e][k][l]));
                                test.Name = "test" + (l * k);
                                test.X1 = srodki[i - 1][l][0];
                                test.Y1 = srodki[i - 1][l][1];
                                test.X2 = srodki[i][k][0];
                                test.Y2 = srodki[i][k][1];

                                test.MouseEnter += MyMouseEnter;
                                test.MouseLeave += MyMouseLeave;
                                Plotno.Children.Add(test);
                                Canvas.SetZIndex(test, 0);
                            }
                        }
                    }
                }
            }
            for (int i = 0; i < LMax; i++)
            {
                for (int j = 0; j < signalY[i][e][vect].GetLength(0); j++)
                {
                    textBlock1.Text += sygnaly[i][e][vect][j]+"\n";
                }
            }
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            if (textBox1.Text != "" && textBox2.Text != "")
            {
                Schemat(weights, signalY);
            }
            else
            {
                MessageBox.Show("Wypełnij oba pola!");
            }
        }

        private void MyMouseEnter(object sender, MouseEventArgs e)
        {
            if (sender.GetType().ToString() == "System.Windows.Shapes.Line")
            {
                Line L = (Line)sender;
                if (L.Stroke.Equals(System.Windows.Media.Brushes.Blue))
                {
                    textBox3.Text += "-";
                }
                textBox3.Text += Convert.ToString(Math.Pow(L.StrokeThickness, 2));
            }
            else
            {
                Ellipse E = (Ellipse)sender;
                if (E.Fill.ToString() == Colors.SkyBlue.ToString())
                {
                    textBox3.Text += "-";
                }
                textBox3.Text += Convert.ToString(Math.Pow(E.Width / promien, 2));
            }
        }

        private void MyMouseLeave(object sender, MouseEventArgs e)
        {
            textBox3.Text = "";
        }

        private void Okno1_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            Okno1.Height = Okno1.ActualHeight;
            Okno1.Width = Okno1.ActualWidth;
            SiatkaGlowna.Height = Okno1.Height;
            SiatkaGlowna.Width = Okno1.Width;
        }

        private void button2_Click(object sender, RoutedEventArgs e)
        {
            Rect rectangle = VisualTreeHelper.GetDescendantBounds(Plotno);
            RenderTargetBitmap targetBitmap = new RenderTargetBitmap(
                (int)rectangle.Width, (int)rectangle.Height,
                96, 96, PixelFormats.Default);
            DrawingVisual dv = new DrawingVisual();
            DrawingContext dc = dv.RenderOpen();

            VisualBrush vb = new VisualBrush(Plotno);
            dc.DrawRectangle(vb, null, rectangle);
            dc.Close();

            targetBitmap.Render(dv);

            BmpBitmapEncoder encoder = new BmpBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(targetBitmap));

            FileStream fs = File.Open("foto.bmp", FileMode.OpenOrCreate);
            encoder.Save(fs);
            fs.Close();
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            int temp = Convert.ToInt32(textBox1.Text);
            textBox1.Text = (temp + 1).ToString();
        }
        private void Button_Click_2(object sender, RoutedEventArgs e)
        {
            int temp = Convert.ToInt32(textBox1.Text);
            textBox1.Text = (temp - 1).ToString();
        }
        private void Button_Click_3(object sender, RoutedEventArgs e)
        {
            int temp = Convert.ToInt32(textBox2.Text);
            textBox2.Text = (temp + 1).ToString();
        }
        private void Button_Click_4(object sender, RoutedEventArgs e)
        {
            int temp = Convert.ToInt32(textBox2.Text);
            textBox2.Text = (temp - 1).ToString();
        }
    }
}
